package com.lcz.manage.util;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.lcz.manage.util.exception.CCException;

/**
 * Spring Context 工具类
 * 
 * @author luchunzhou
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringContextUtils.class);

	public static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		SpringContextUtils.applicationContext = applicationContext;
	}

    /**
     * 反射调用spring bean方法
     *
     * @param bean
     * @param methodName
     * @param params
     */
    public static void invokeBeanMethod(Object bean, String methodName, Object... params) {

        try {
            Class<?> cls = bean.getClass();
            Method[] declaredMethods = cls.getDeclaredMethods();

            Method beanMethod = Arrays.stream(declaredMethods)
                .filter(method -> StringUtils.equals(method.getName(), methodName))
                .filter(method -> {
                    int parameterCount = method.getParameterCount();
                    if (parameterCount != params.length) {
                        return false;
                    }

                    Class<?>[] parameterTypes = method.getParameterTypes();
                    List<Object> inputParams = Lists.newArrayList(params);

                    for (int i = 0; i < inputParams.size(); i++) {
                        if (!parameterTypes[i].isAssignableFrom(inputParams.get(i)
                            .getClass())) {
                            return false;
                        }
                    }

                    return true;
                })
                .findFirst()
                .orElse(null);

            if (Objects.isNull(beanMethod)) {
                return;
            }

            beanMethod.setAccessible(true);
            beanMethod.invoke(bean, params);

        } catch (Exception ex) {
            LOGGER.error("ReflectUtils invokeBeanMethod exception", ex);
            throw new CCException("ReflectUtils invokeBeanMethod exception", ex);
        }
    }

	public static Object getBean(String name) {
		return applicationContext.getBean(name);
	}

	public static <T> T getBean(String name, Class<T> requiredType) {
		return applicationContext.getBean(name, requiredType);
	}

	public static boolean containsBean(String name) {
		return applicationContext.containsBean(name);
	}

	public static boolean isSingleton(String name) {
		return applicationContext.isSingleton(name);
	}

	public static Class<? extends Object> getType(String name) {
		return applicationContext.getType(name);
	}

	public static <T> Map<String, T> getBeansOfType(Class<T> var1){
	    return applicationContext.getBeansOfType(var1);
    }

}